#pragma once

#include "task.hpp"
#include "channel.hpp"
#include <iostream>
#include <string>
#include <vector>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <functional>
#include "log.hpp"

namespace ProcessPoolModule
{
    using work_t = std::function<void()>;
    // 定义状态码
    enum
    {
        OK = 0,
        UsageError,
        PipeError,
        ForkError
    };

    class ProcessPool
    {
    public:
        ProcessPool(int n, work_t w) : _processnum(n), _work(w)
        {
        }
        int InitProcess()
        {
            // 创建进程
            for (int i = 0; i < _processnum; ++i)
            {
                // 先创建管道
                int pipefd[2] = {0};
                int n = pipe(pipefd);
                if (n < 0)
                    return PipeError;
                // 创建进程
                pid_t pid = fork();
                if (pid < 0)
                    return ForkError;
                // 子进程关闭写端
                if (pid == 0)
                {
                    // 关闭历史写端
                    for (auto &c : _channels)
                    {
                        c.Close();
                    }
                    ::close(pipefd[1]);
                    // 重定向
                    dup2(pipefd[0], 0);
                    // 执行任务
                    _work();
                    // 执行完任务后退出
                    ::exit(0);
                }
                // 父进程关闭读端
                ::close(pipefd[0]);
                // 历史写端更新
                _channels.emplace_back(pipefd[1], pid);
            }
            return OK;
        }

        // 分发任务
        void DispatchTask(int num) // 任务数
        {
            int worker_num = 0;
            while (num--)
            {
                int task = tm.SelectTask();
                Channel &ch = _channels[worker_num++];
                worker_num %= _channels.size();
                ch.Send(task);
            }
        }
        // 清理进程池
        void CleanProcessPool()
        {
            for (auto &c : _channels)
            {
                c.Close();
                pid_t rid = ::waitpid(c.Id(), nullptr, 0);
                if (rid > 0)
                {
                    LogModule::LOG(LogModule::INFO) << "wait pid: " << rid << " success";
                }
            }
        }
        void DebugPrint()
        {
            for (auto &c : _channels)
            {
                LogModule::LOG(LogModule::INFO) << "channel: " << c.Name();
            }
        }

    private:
        std::vector<Channel> _channels;
        int _processnum;
        work_t _work;
    };
}
